package com.bjy.qa.service.performancetest.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bjy.qa.dao.functionaltest.AgentDao;
import com.bjy.qa.dao.performancetest.PerfTestResultDao;
import com.bjy.qa.dao.performancetest.PerfTestResultTestScriptDao;
import com.bjy.qa.dao.performancetest.PerfTestSuiteDao;
import com.bjy.qa.dao.performancetest.ScenarioDao;
import com.bjy.qa.dao.user.UserDao;
import com.bjy.qa.entity.MyPage;
import com.bjy.qa.entity.ResponsePagingData;
import com.bjy.qa.entity.functionaltest.*;
import com.bjy.qa.entity.performancetest.*;
import com.bjy.qa.entity.user.User;
import com.bjy.qa.enumtype.AgentStatus;
import com.bjy.qa.enumtype.CatalogType;
import com.bjy.qa.enumtype.RunStatus;
import com.bjy.qa.exception.MyException;
import com.bjy.qa.service.functionaltest.IAgentService;
import com.bjy.qa.service.functionaltest.ITestSuiteService;
import com.bjy.qa.service.performancetest.IPerfTestResultTestScriptService;
import com.bjy.qa.service.performancetest.IPerfTestSuiteService;
import com.bjy.qa.service.performancetest.IPerfTestSuiteTestScriptGeneratorService;
import com.bjy.qa.service.performancetest.IPerfTestSuiteTestScriptService;
import com.bjy.qa.util.TimeUtil;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.Serializable;
import java.util.*;

@Service
public class PerfTestSuiteService extends ServiceImpl<PerfTestSuiteDao, PerfTestSuite> implements IPerfTestSuiteService {
    @Resource
    PerfTestSuiteDao perfTestSuiteDao;
    @Resource
    IPerfTestSuiteTestScriptService perfTestSuiteTestScriptService;
    @Resource
    ScenarioDao scenarioDao;
    @Resource
    IPerfTestSuiteTestScriptGeneratorService perfTestSuiteTestScriptGeneratorService;
    @Resource
    AgentDao agentDao;
    @Resource
    IAgentService iAgentService;
    @Resource
    ITestSuiteService iTestSuiteService;
    @Resource
    PerfTestResultDao perfTestResultDao;
    @Resource
    PerfTestResultTestScriptDao perfTestResultTestScriptDao;
    @Resource
    IPerfTestResultTestScriptService iPerfTestResultTestScriptService;
    @Resource
    UserDao userDao;

    @Override
    public ResponsePagingData list(MyPage<PerfTestSuite> myPage) {
        Page<PerfTestSuite> page = new Page(myPage.getPageNum(), myPage.getPageSize()); // 构造待查询 page 对象
        page.setOrders(myPage.getOrders()); // 设置排序字段

        // 构造待查询 QueryWrapper
        PerfTestSuite perfTestSuite = myPage.getQuery();
        QueryWrapper<PerfTestSuite> queryWrapper = new QueryWrapper<>();
        if (perfTestSuite != null) {
            if (perfTestSuite.getProjectId() == null) {
                throw new MyException("projectId: 项目信息 id 必填!");
            } else {
                queryWrapper.eq("project_id", perfTestSuite.getProjectId());
            }

            if (StringUtils.isNotBlank(perfTestSuite.getName())) {
                queryWrapper.like("name", perfTestSuite.getName());
            }
        }
        queryWrapper.orderByDesc("id");

        page = perfTestSuiteDao.selectPage(page, queryWrapper); // 按分页查询

        myPage.setTotalRecords(page.getTotal()); // 设置返回总记录数

        return new ResponsePagingData(myPage, page.getRecords());
    }

    @Override
    public List<PerfTestSuite> listAll(Long projectId) {
        QueryWrapper<PerfTestSuite> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("project_id", projectId);
        queryWrapper.orderByDesc("id");
        return perfTestSuiteDao.selectList(queryWrapper);
    }

    @Override
    public PerfTestSuite findById(Long id) {
        PerfTestSuite perfTestSuite = perfTestSuiteDao.findById(id);
        if (perfTestSuite == null) {
            perfTestSuite = perfTestSuiteDao.selectById(id);
            if (perfTestSuite != null) {
                perfTestSuite.setTestScripts(new ArrayList<>());
            }
        }
        return perfTestSuite;
    }

    @Override
    public int add(PerfTestSuite perfTestSuite) {
        int ret = perfTestSuiteDao.insert(perfTestSuite);

        Long projectId = perfTestSuite.getProjectId();

        // 新增 或 更新 性能测试套件 对应 性能测试脚本
        List<PerfTestSuiteTestScript> perfTestSuiteTestScriptList = new ArrayList<>();
        int sort = 1;
        for (TestScript testScript : perfTestSuite.getTestScripts()) {
            PerfTestSuiteTestScript perfTestSuiteTestScript = new PerfTestSuiteTestScript();
            perfTestSuiteTestScript.setTestScriptId(testScript.getId());
            perfTestSuiteTestScript.setPerfTestSuiteId(perfTestSuite.getId());
            perfTestSuiteTestScript.setSort(sort++);

            // 新增 或 更新 性能测试场景
            if (testScript.getScenario() != null) {
                Scenario scenario = testScript.getScenario();
                scenario.setProjectId(projectId);
                scenario.setUpdatedAt(null);
                scenario.setCreatedAt(null);
                scenarioDao.insert(scenario);
                perfTestSuiteTestScript.setScenarioId(scenario.getId());
            } else {
                throw new MyException("测试用例 ID: " + testScript.getId() + " 中的 性能测试场景 为空，必填！");
            }

            perfTestSuiteTestScriptList.add(perfTestSuiteTestScript);
        }
        perfTestSuiteTestScriptService.saveBatch(perfTestSuiteTestScriptList);

        // 新增 或 更新 压力机（load generators）
        for (int i = 0; i < perfTestSuite.getTestScripts().size(); i++) {
            TestScript testScript = perfTestSuite.getTestScripts().get(i);
            if (testScript.getGenerators() != null) {
                List<PerfTestSuiteTestScriptGenerator> perfTestSuiteTestScriptGeneratorList = new ArrayList<>();
                for (Generator generator : testScript.getGenerators()) {
                    PerfTestSuiteTestScriptGenerator perfTestSuiteTestScriptGenerator = new PerfTestSuiteTestScriptGenerator();
                    perfTestSuiteTestScriptGenerator.setPerfTestSuiteTestScriptId(perfTestSuiteTestScriptList.get(i).getId());
                    perfTestSuiteTestScriptGenerator.setGeneratorId(generator.getId());

                    perfTestSuiteTestScriptGeneratorList.add(perfTestSuiteTestScriptGenerator);
                }
                perfTestSuiteTestScriptGeneratorService.saveBatch(perfTestSuiteTestScriptGeneratorList);
            }
        }

        return ret;
    }

    @Override
    public int update(PerfTestSuite perfTestSuite) {
        // 更新数据需要清除 UpdatedTime 和 CreatedTime，否则不会自动更新修改时间
        perfTestSuite.setUpdatedAt(null);
        perfTestSuite.setCreatedAt(null);

        if (perfTestSuite.getRobotId() == null) {
            perfTestSuite.setRobotId(0L);
        }

        Long projectId = perfTestSuite.getProjectId();

        // 新增 或 更新 性能测试套件 对应 性能测试脚本
        List<PerfTestSuiteTestScript> perfTestSuiteTestScriptList = new ArrayList<>();
        int sort = 1;
        for (TestScript testScript : perfTestSuite.getTestScripts()) {
            PerfTestSuiteTestScript perfTestSuiteTestScript = new PerfTestSuiteTestScript();
            perfTestSuiteTestScript.setTestScriptId(testScript.getId());
            perfTestSuiteTestScript.setPerfTestSuiteId(perfTestSuite.getId());
            perfTestSuiteTestScript.setSort(sort++);

            // 新增 或 更新 性能测试场景
            if (testScript.getScenario() != null) {
                if (testScript.getScenario().getId() == null || testScript.getScenario().getId() == 0) {
                    Scenario scenario = testScript.getScenario();
                    scenario.setProjectId(projectId);
                    scenario.setUpdatedAt(null);
                    scenario.setCreatedAt(null);
                    scenarioDao.insert(scenario);
                    perfTestSuiteTestScript.setScenarioId(scenario.getId());
                } else {
                    Scenario scenario = testScript.getScenario();
                    scenario.setUpdatedAt(null);
                    scenario.setCreatedAt(null);
                    scenarioDao.updateById(scenario);
                    perfTestSuiteTestScript.setScenarioId(testScript.getScenario().getId());
                }
            } else {
                throw new MyException("测试用例 ID: " + testScript.getId() + " 中的 性能测试场景 为空，必填！");
            }

            perfTestSuiteTestScriptList.add(perfTestSuiteTestScript);
        }
        // TODO: 2024/1/26 后续改为自定义批量更新
        QueryWrapper<PerfTestSuiteTestScript> queryPerfTestSuiteTestScriptWrapper = new QueryWrapper<>();
        queryPerfTestSuiteTestScriptWrapper.eq("perf_test_suite_id", perfTestSuite.getId());
        perfTestSuiteTestScriptService.remove(queryPerfTestSuiteTestScriptWrapper);
        perfTestSuiteTestScriptService.saveBatch(perfTestSuiteTestScriptList);

        // 新增 或 更新 压力机（load generators）
        for (int i = 0; i < perfTestSuite.getTestScripts().size(); i++) {
            TestScript testScript = perfTestSuite.getTestScripts().get(i);
            if (testScript.getGenerators() != null) {
                List<PerfTestSuiteTestScriptGenerator> perfTestSuiteTestScriptGeneratorList = new ArrayList<>();
                for (Generator generator : testScript.getGenerators()) {
                    PerfTestSuiteTestScriptGenerator perfTestSuiteTestScriptGenerator = new PerfTestSuiteTestScriptGenerator();
                    perfTestSuiteTestScriptGenerator.setPerfTestSuiteTestScriptId(perfTestSuiteTestScriptList.get(i).getId());
                    perfTestSuiteTestScriptGenerator.setGeneratorId(generator.getId());

                    perfTestSuiteTestScriptGeneratorList.add(perfTestSuiteTestScriptGenerator);
                }
                // TODO: 2024/1/26 后续改为自定义批量更新
                QueryWrapper<PerfTestSuiteTestScriptGenerator> queryPerfTestSuiteTestScriptGeneratorWrapper = new QueryWrapper<>();
                queryPerfTestSuiteTestScriptGeneratorWrapper.eq("perf_test_suite_test_script_id", perfTestSuite.getId());
                perfTestSuiteTestScriptGeneratorService.remove(queryPerfTestSuiteTestScriptGeneratorWrapper);
                perfTestSuiteTestScriptGeneratorService.saveBatch(perfTestSuiteTestScriptGeneratorList);
            }
        }

        return perfTestSuiteDao.updateById(perfTestSuite);
    }

    @Override
    public Boolean delete(Long id) {
        perfTestSuiteDao.deleteById(id); // 删除 性能测试套件（pt_test_suite）

        // 查询性能测试套件 对应 性能测试脚本 ids
        QueryWrapper<PerfTestSuiteTestScript> perfTestSuiteTestScriptQueryWrapper = new QueryWrapper<>();
        perfTestSuiteTestScriptQueryWrapper.eq("perf_test_suite_id", id);
        List<PerfTestSuiteTestScript> perfTestSuiteTestScriptList = perfTestSuiteTestScriptService.list(perfTestSuiteTestScriptQueryWrapper);
        List<Long> perfTestSuiteTestScriptIds = new ArrayList<>();
        for (PerfTestSuiteTestScript perfTestSuiteTestScript : perfTestSuiteTestScriptList) {
            perfTestSuiteTestScriptIds.add(perfTestSuiteTestScript.getId());
        }

        // 删除 性能测试套件 对应 性能测试脚本（pt_test_suite_test_script）
        perfTestSuiteTestScriptService.remove(perfTestSuiteTestScriptQueryWrapper);

        // 删除 性能测试套件 中 性能测试脚本 对应 压力机 （pt_test_suite_test_script_generator）
        if (perfTestSuiteTestScriptIds.size() > 0) {
            QueryWrapper<PerfTestSuiteTestScriptGenerator> perfTestSuiteTestScriptGeneratorQueryWrapper = new QueryWrapper<>();
            perfTestSuiteTestScriptGeneratorQueryWrapper.in("perf_test_suite_test_script_id", perfTestSuiteTestScriptIds);
            perfTestSuiteTestScriptGeneratorService.remove(perfTestSuiteTestScriptGeneratorQueryWrapper);
        }

        return true;
    }

    @Override
    public boolean run(Long projectId, Long userId, Long id) {
        // 查询要执行的 性能测试套件
        PerfTestSuite perfTestSuite = perfTestSuiteDao.findById(id);
        if (perfTestSuite == null) {
            perfTestSuite = perfTestSuiteDao.selectById(id);
            if (perfTestSuite != null) {
                perfTestSuite.setTestScripts(new ArrayList<>());
            }
        }
        if (perfTestSuite == null) {
            throw new MyException("性能测试套件 ID 不存在！");
        }

        // 执行测试脚本的 agent
        if (perfTestSuite.getAgentId() == null || perfTestSuite.getAgentId() == 0) {
            throw new MyException("性能测试套件中没有关联 Agent！");
        }
        Agent agent = agentDao.selectById(perfTestSuite.getAgentId());
        if (agent.getStatus() == AgentStatus.OFF_LINE) {
            throw new MyException("Agent 不在线，Agent name：" + agent.getName());
        }

        // 待运行的测试脚本
        List<TestScript> testScriptList = perfTestSuite.getTestScripts();
        if (testScriptList.size() == 0) {
            throw new MyException("性能测试套件中没有关联测试脚本！");
        }

        User user = userDao.selectById((Serializable) userId); // 根据 执行套件 用户 ID，查询用户信息

        // 保存 suite 数据，创建 result 表
        PerfTestResult perfTestResult = new PerfTestResult();
        perfTestResult.setSuiteId(perfTestSuite.getId());
        perfTestResult.setSuiteName(perfTestSuite.getName());
        perfTestResult.setProjectId(perfTestSuite.getProjectId());
        perfTestResult.setRunUser(user.getName());
        perfTestResult.setStatus(RunStatus.RUNNING.getValue()); // 设置置为运行状态
        perfTestResult.setCaseType(CatalogType.PERFORMANCE_TEST_SCRIPT.getValue());
        perfTestResultDao.insert(perfTestResult);

        // 获取参数
        Map<String, String> globalParamMap = new HashMap<>(); // 全局参数（环境参数 + 静态参数 + 用户参数 + 套件唯一参数）
        List<GlobalParam> caseUniqUniquenessParams = new ArrayList<>(); // 用例唯一参数列表
        int uniqueValue = iTestSuiteService.getParamMap(perfTestSuite.getProjectId(), perfTestSuite.getEnv(), userId, globalParamMap, caseUniqUniquenessParams); // 根据 项目id 和 环境，得到参数 map

        List<PerfTestResultTestScript> perfTestResultTestScriptList = new ArrayList<>(); // 构造 测试套件对应测试用例 数据
        List<TestScriptDTO> testScriptDTOList = new ArrayList<>(); // 构造 TestScriptDTO
        for (TestScript testScript : testScriptList) {
            // 构造 测试套件对应测试用例 数据
            PerfTestResultTestScript perfTestResultTestScript = new PerfTestResultTestScript();
            perfTestResultTestScript.setResultId(perfTestResult.getId());
            perfTestResultTestScript.setTestScriptId(testScript.getId());
            perfTestResultTestScript.setAgent(agent.getId());
            perfTestResultTestScript.setStatus(RunStatus.WAITING.getValue());
            perfTestResultTestScriptList.add(perfTestResultTestScript);

            // 准备下发脚本
            TestScriptDTO testScriptDTO = new TestScriptDTO();
            testScriptDTO.setRid(perfTestResult.getId());
            testScriptDTO.setCid(testScript.getId());
            testScriptDTO.setPid(projectId);
            testScriptDTO.setScriptType(testScript.getScriptType().getValue());
            testScriptDTO.setName(testScript.getName());
            testScriptDTO.setDataProvider(testScript.getDataProvider());
            testScriptDTO.setContent(testScript.getContent());
            testScriptDTO.setGenerators(testScript.getGenerators());
            testScriptDTO.setScenario(testScript.getScenario());

            // 产生用例唯一参数，并设置全局变量
            for (GlobalParam globalParam : caseUniqUniquenessParams) {
                globalParamMap.put(globalParam.getParamName(), TimeUtil.generateTimestampParameter(globalParam.getLength(), uniqueValue++));
            }
            testScriptDTO.setGp((Map<String, String>) ((HashMap<String, String>) globalParamMap).clone());

            testScriptDTOList.add(testScriptDTO);
        }

        iPerfTestResultTestScriptService.saveBatch(perfTestResultTestScriptList); // 保存 测试套件对应测试用例 数据

        RunTestSuite runTestSuite = new RunTestSuite();
        runTestSuite.setCaseType(CatalogType.PERFORMANCE_TEST_SCRIPT.getValue());
        runTestSuite.setMsg("perfSuite");
        runTestSuite.setCases(testScriptDTOList);

        iAgentService.pubCmd(agent.getKey(), "runPerfSuite", JSON.toJSONString(runTestSuite)); // 下发套件
        return true;
    }

    @Override
    public boolean forceStop(Long id, String runUser) {
        // 查询测试结果数据
        PerfTestResult testResultSuite = perfTestResultDao.selectById(id); // 根据 suite id 查找 suite
        if (testResultSuite == null) {
            throw new MyException("性能测试结果停止失败：测试结果 ID: " + id + " 不存在！");
        }
        Long suiteId = testResultSuite.getSuiteId();
        if (suiteId == -9999L) {
            throw new MyException("性能测试结果停止失败：无法停止 DEBUG 测试结果！");
        }

        // 查询测试套件
        PerfTestSuite testSuite = perfTestSuiteDao.findById(testResultSuite.getSuiteId()); // 根据 suite id 查找 suite
        if (testSuite == null) {
            throw new MyException("性能测试结果停止失败：Suite ID: " + suiteId + " 不存在或已删除！");
        }
        if (testSuite.getTestScripts().size() == 0) {
            throw new MyException("性能测试结果停止失败：性能测试套件未关联脚本，无须停止！！");
        }

        // 查询测试套件对应的 agent
        Agent agent = agentDao.selectById(testSuite.getAgentId());
        if (agent.getStatus() == AgentStatus.OFF_LINE) {
            throw new MyException("性能测试结果停止失败：性能测试套件关联的 Agent 不在线！");
        }

        // 更新测试结果为 - 中断
        testResultSuite.setStatus(RunStatus.BROKEN.getValue());
        testResultSuite.setRunUser(runUser);
        testResultSuite.setEndAt(new Date());
        testResultSuite.setTotalTime(TimeUtil.getTimeDiff(testResultSuite.getCreatedAt(), new Date()));
        perfTestResultDao.updateById(testResultSuite);

        // 更新测试结果关联测试用例中未运行完成的为 - 中断
        QueryWrapper<PerfTestResultTestScript> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("result_id", testResultSuite.getId()).and(wrapper ->
                wrapper.eq("status", RunStatus.RUNNING.getValue())
                        .or().eq("status", RunStatus.WAITING.getValue())
        );
        List<PerfTestResultTestScript> testResultCaseList = perfTestResultTestScriptDao.selectList(queryWrapper);
        for (PerfTestResultTestScript temp : testResultCaseList) {
            temp.setStatus(RunStatus.BROKEN.getValue());
        }
        iPerfTestResultTestScriptService.updateBatchById(testResultCaseList);

        // 组装下发数据
        List<JSONObject> suiteDetail = new ArrayList<>();
        for (TestScript testScript : testSuite.getTestScripts()) {
            JSONObject suite = new JSONObject();
            suite.put("cid", testScript.getId());
            suite.put("rid", id);
            suiteDetail.add(suite);
        }
        JSONObject result = new JSONObject();
        result.put("msg", "forceStopSuite");
        result.put("type", CatalogType.PERFORMANCE_TEST_SCRIPT.getValue());
        result.put("cases", suiteDetail);

        iAgentService.pubCmd(agent.getKey(), "forceStopPubCmd", JSON.toJSONString(result)); // 下发给 agent
        return true;
    }

    @Override
    public PerfTestResult updateBaseLine(Long id) {
        // 根据 id 查询 性能测试结果
        PerfTestResult perfTestResult = perfTestResultDao.selectById(id);
        if (perfTestResult.getBaseLine() != null && perfTestResult.getBaseLine()) {
            perfTestResult.setBaseLine(false);
        } else {
            perfTestResult.setBaseLine(true);
        }

        // 将此 suite_id 下的其它 性能测试结果 改为 false
        if (perfTestResult.getBaseLine()) {
            List<PerfTestResult> perfTestResultList = perfTestResultDao.selectPerfTestResultBaseLine(perfTestResult.getSuiteId());
            for (PerfTestResult temp : perfTestResultList) {
                temp.setBaseLine(false);
                perfTestResultDao.updateById(temp);
            }
        }

        perfTestResultDao.updateById(perfTestResult); // 更新 id 的 base_line
        return perfTestResult;
    }
}
